package jmine.tec.datadigester.positional.meta;

import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;

import org.apache.commons.beanutils.BeanUtils;

import bancosys.tec.datadigester.DataDigesterException;

/**
 * Metadata about a positional field
 * 
 * @author lundberg
 */
public abstract class PositionalFieldMetadata implements Comparable<PositionalFieldMetadata> {

    private final PropertyDescriptor property;

    private final String description;

    private final int start;

    private final int size;

    /**
     * Constructor
     * 
     * @param property property
     * @param description description
     * @param start start
     * @param size size
     */
    public PositionalFieldMetadata(PropertyDescriptor property, String description, int start, int size) {
        this.property = property;
        this.description = description;
        this.start = start;
        this.size = size;
        this.checkPropertyCompatibility();
    }

    /**
     * Checks if the given property is compatible with this field metadata type
     */
    protected void checkPropertyCompatibility() {
        if (!this.getFieldType().isAssignableFrom(this.property.getPropertyType())) {
            throw new IllegalArgumentException("Wrong property type, expected " + this.getFieldType() + ", actual "
                    + this.property.getPropertyType());
        }
    }

    /**
     * Expected field type
     * 
     * @return field type
     */
    protected abstract Class<?> getFieldType();

    /**
     * @return value type
     */
    public Class<?> getValueType() {
        return this.property.getPropertyType();
    }

    /**
     * Reads from the given bean
     * 
     * @param bean bean
     * @return value
     */
    public Object get(Object bean) {
        try {
            return BeanUtils.getProperty(bean, this.name());
        } catch (IllegalAccessException e) {
            throw new DataDigesterException(e);
        } catch (InvocationTargetException e) {
            throw new DataDigesterException(e);
        } catch (NoSuchMethodException e) {
            throw new DataDigesterException(e);
        }
    }

    /**
     * {@inheritDoc}
     */
    public int compareTo(PositionalFieldMetadata o) {
        return this.start - o.start();
    }

    /**
     * @return property descriptor
     */
    protected PropertyDescriptor getProperty() {
        return this.property;
    }

    /**
     * @return the name
     */
    public String name() {
        return this.property.getName();
    }

    /**
     * @return the start
     */
    public int start() {
        return this.start;
    }

    /**
     * @return the size
     */
    public int size() {
        return this.size;
    }

    /**
     * @return description
     */
    public String description() {
        return this.description;
    }

}
